﻿using HPSocketCS;
using log4net;
using McsUtility;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace CommServer.Common
{
	public class Server
	{
		ILog logger = LogManager.GetLogger(typeof(Program));
		public event EventHandler<MsgEventArgs> OnMessage;
		public bool IsStart { get { return server.IsStarted; } }

		private Task ObserverTask;
		private CancellationTokenSource ObserverTokenSource;

		protected TcpPackServer server = new TcpPackServer();

		/// <summary>
		/// 本地IP
		/// </summary>
		public string LocalIp { get; set; }

		/// <summary>
		/// 本地端口
		/// </summary>
		public ushort LocalPort { get; set; }

		/// <summary>
		/// 最大连接量
		/// </summary>
		public uint MaxConnectionCount
		{
			get
			{
				return server.MaxConnectionCount;
			}
			set
			{
				server.MaxConnectionCount = value;
			}
		}

		//客户端ID与代理ID的映射（方便快速找到某连接在哪个代理服务器中）
		//ConcurrentDictionary<int, ClientData> ClientMap = new ConcurrentDictionary<int, ClientData>();

		public Server()
		{
			//设置服务器事件
			server.MaxPackSize = 0x1000;
			server.PackHeaderFlag = 0xff;
			server.OnPrepareListen += new TcpServerEvent.OnPrepareListenEventHandler(OnServerPrepareListen);
			server.OnAccept += new TcpServerEvent.OnAcceptEventHandler(OnServerAccept);
			server.OnSend += new TcpServerEvent.OnSendEventHandler(OnServerSend);
			server.OnReceive += new TcpServerEvent.OnReceiveEventHandler(OnServerReceive);
			server.OnClose += new TcpServerEvent.OnCloseEventHandler(OnServerClose);
			server.OnShutdown += new TcpServerEvent.OnShutdownEventHandler(OnServerShutdown);
		}

		private void InternalInit()
		{
		}

		#region void Start()

		public void Start()
		{
			if (server.IsStarted)
			{
				throw new Exception(message: "The server has been started");
			}

			if (string.IsNullOrEmpty(LocalIp) || LocalPort == 0)
			{
				throw new Exception(message: "Please Set[LocalIp,LocalPort,Whitelist]");
			}

			InternalInit();

			server.IpAddress = LocalIp;
			server.Port = LocalPort;
			server.MaxConnectionCount = MaxConnectionCount;

			if (server.Start())
			{
				StartObserver();
				AddMsg(string.Format(" > Server Start OK -> ({0}:{1})", LocalIp, LocalPort));
			}
			else
			{
				AddMsg(string.Format(" > Server start fail -> {0}({1})", server.ErrorMessage, server.ErrorCode));
			}
		}

		#endregion

		#region void Stop()

		public void Stop()
		{
			StopObserver();
			server.Stop();
			if (!IsStart)
			{
				AddMsg("$Server Stop");
			}
			else
			{
				AddMsg(string.Format("$Stop Error -> {0}({1})", server.ErrorMessage, server.ErrorCode));
			}
		}

		#endregion

		#region string ErrorMessage()

		public string ErrorMessage()
		{
			return server.ErrorMessage;
		}

		#endregion

		#region SocketError ErrorCode()

		public SocketError ErrorCode()
		{
			return server.ErrorCode;
		}

		#endregion

		#region private void AddMsg(string msg)

		private void AddMsg(string msg)
		{
			OnMessage?.Invoke(this, new MsgEventArgs(msg));
		}

		#endregion

		#region 服务器事件

		protected virtual HandleResult OnServerPrepareListen(IntPtr soListen)
		{
			return HandleResult.Ok;
		}

		#region protected virtual HandleResult OnServerAccept(IntPtr connId, IntPtr pClient)

		protected virtual HandleResult OnServerAccept(IntPtr connId, IntPtr pClient)
		{
			string ip = string.Empty;
			ushort port = 0;
			if (server.GetRemoteAddress(connId, ref ip, ref port))
			{
				//logger.Info(string.Format("IP{0}，端口号{1}请求连接。", ip, port));

				string whiteString = System.Configuration.ConfigurationManager.AppSettings["WhiteList"];
				List<string> Whitelist = whiteString.Split(',').ToList();

				if (Whitelist.Exists(m => m.Equals(ip)))
				{
					ClientInfo clientInfo = new ClientInfo { ConnId = connId, IpAddress = ip, Port = port };
					if (server.SetExtra(connId, clientInfo))
					{
						AddMsg(string.Format(" > [{0},OnAccept] -> PASS({1}:{2})", connId, ip.ToString(), port));
						return HandleResult.Ok;
					}
					else
					{
						AddMsg(string.Format(" > [{0},OnAccept] -> SetConnectionExtra fail", connId));
						return HandleResult.Error;
					}
				}
				else
				{
					AddMsg(string.Format(" > [{0},OnAccept] -> Not in Whitelist", connId));
					return HandleResult.Error;
				}
			}
			else
			{
				AddMsg(string.Format(" > [{0},OnAccept] -> Server_GetClientAddress() Error", connId));
				return HandleResult.Error;
			}
		}

		#endregion

		#region protected virtual HandleResult OnServerSend(IntPtr connId, byte[] bytes)

		protected virtual HandleResult OnServerSend(IntPtr connId, byte[] bytes)
		{
			//AddMsg(string.Format(" > [{0},OnServerSend]", connId));
			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnServerReceive(IntPtr connId, byte[] bytes) 接收信息

		protected virtual HandleResult OnServerReceive(IntPtr connId, byte[] bytes)
		{
			ProcessPacket(connId, bytes);
			return HandleResult.Ok;
		}

		private void ProcessPacket(IntPtr connId, byte[] bytes)
		{
			//AddMsg(string.Format(" > [{0},ProcessPacket] -> bytes({1})", connId, bytes.Length));

			var proto = ProtocolHelper.GetProtoType(bytes, body: out byte[] body);
			string json = System.Text.Encoding.UTF8.GetString(body);
			//logger.Info(proto.ToString() + Environment.NewLine + "    " + json);

			switch (proto)
			{
				case ProtoType.NodeServerRequestConnId://NodeServer连接，获取ConnId
					ProcessNodeServerRequestConnId(connId, body);
					break;
				case ProtoType.NodeServerInfoToCommServer://NodeServer通知CommServerIP信息
					ProcessNodeServerInfoToCommServer(connId, body);
					break;
				case ProtoType.MinerLoginCommServer://管理端/矿机登录通知CommServer
					ProcessMinerLoginCommServer(connId, body);
					break;
				case ProtoType.MinerCloseCommServer://管理端/矿机掉线通知CommServer
					ProcessMinerCloseCommServer(connId, body);
					break;
				case ProtoType.MinerClientStartMiningToCommServer://矿机挖矿状态发送给CommServer
					ProcessMinerClientStartMiningToCommServer(connId, body);
					break;
				case ProtoType.MinerManagerStartMiningCommServer://开始挖矿转到CommServer
					ProcessMinerManagerStartMiningCommServer(connId, body);
					break;
				case ProtoType.MinerManagerEndMiningCommServer://停止挖矿转到CommServer
					ProcessMinerManagerEndMiningCommServer(connId, body);
					break;
				case ProtoType.MinerManagerResetCommServer://重启机器转到CommServer
					ProcessMinerManagerResetCommServer(connId, body);
					break;
				case ProtoType.MinerManagerModifyBatchCommServer://批量修改矿池转到CommServer【暂未启用】
					ProcessMinerManagerModifyBatchCommServer(connId, body);
					break;
				case ProtoType.MinerManagerModifyOrePoolUrlCommServer://修改矿池转到CommServer（高级配置）
					ProcessMinerManagerModifyOrePoolUrlCommServer(connId, body);
					break;
				case ProtoType.MinerClientEndMiningToCommServer://矿机停止挖矿状态发送给CommServer
					ProcessMinerClientEndMiningToCommServer(connId, body);
					break;
				case ProtoType.GetNodeServerCommServer://CommServer响应询问可连接NodeServer
					ProcessGetNodeServerCommServer(connId, body);
					break;
				case ProtoType.NodeServerWarnToCommServer://报警转交CommServer
					ProcessNodeServerWarnToCommServer(connId, body);
					break;
				default://不符合接收规则时执行
					this.server.Disconnect(connId);
					logger.Info(string.Format("不符合规则的Receive：{0}{1}", proto, Environment.NewLine + "    " + json));
					break;
			}
		}

		#region void ProcessNodeServerRequestConnId(IntPtr connId, byte[] body) NodeServer连接，获取ConnId

		private void ProcessNodeServerRequestConnId(IntPtr connId, byte[] body)
		{
			//NodeServerRequestConnId = 1 NodeServer连接，获取ConnId
			//CommServerResponseConnId = 2 CommServer响应NodeServer请求，发送ConnId

			try
			{
				var package = ProtocolHelper.WritePackage(ProtoType.CommServerResponseConnId, connId.ToString());
				this.server.Send(connId, package, package.Length);
			}
			catch (Exception e)
			{
				logger.Error("NodeServerRequestConnId" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessNodeServerInfoToCommServer(IntPtr connId, byte[] body) NodeServer通知CommServerIP信息

		private void ProcessNodeServerInfoToCommServer(IntPtr connId, byte[] body)
		{
			//NodeServerInfoToCommServer = 67 NodeServer通知CommServerIP信息

			try
			{
				McsUtility.Models.GetNodeServerResponseModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.GetNodeServerResponseModel>(body);

				//添加NodeServer信息
				Models.NodeServerModel info = new Models.NodeServerModel
				{
					CommServerConnId = (int)connId,
					IP = result.IP,
					Port = result.Port
				};
				Common.Data.NodeServer.AddOrUpdate((int)connId, info, (FKey, FValue) => { return info; });
			}
			catch (Exception e)
			{
				logger.Error("NodeServerInfoToCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerLoginCommServer(IntPtr connId, byte[] body) 管理端/矿机登录通知CommServer

		private void ProcessMinerLoginCommServer(IntPtr connId, byte[] body)
		{
			//MinerLoginCommServer = 15 管理端/矿机登录通知CommServer
			//CommServerMinerLoginToNodeServer = 55 CommonServer下发矿机登录信息给NodeServer
			//MinerManagerDownToNodeServer = 71 管理端已经登录，发送NodeServer响应管理端的ConnId

			try
			{
				McsUtility.Models.LoginResultForCommServerModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.LoginResultForCommServerModel>(body);

				string json = ProtocolHelper.ReadPackageBody(body);
				//logger.Info("MinerLoginCommServer 接收 ConnId:" + connId.ToInt32() + Environment.NewLine + json);

				#region 管理端登录

				if (result.ClientType == ClientType.AdminClient)
				{
					List<Models.ManagerModel> infosManager = null;
					if (Common.Data.Manager.ContainsKey(result.UserID))
						Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

					//检测是否存在相同数据，清除相同数据
					if (infosManager.Count > 0)
					{
						int index = infosManager.FindIndex(p => p.CommServerConnId == (int)connId && p.NodeServerConnId == result.connID);
						if (index >= 0)
							infosManager.RemoveAt(index);
					}

					//缓存管理端信息
					Models.ManagerModel info = new Models.ManagerModel
					{
						CommServerConnId = (int)connId,
						NodeServerConnId = result.connID,
						UserId = result.UserID,
						UserName = result.Name,
					};

					if (infosManager != null)
						infosManager.Add(info);
					else
						infosManager = new List<Models.ManagerModel> { info };

					Common.Data.Manager.AddOrUpdate(info.UserId, infosManager, (FKey, FValue) => { return infosManager; });
				}

				#region 互踢操作【暂未启用】

				//if (result.ClientType == ClientType.AdminClient)
				//{
				//	Models.ManagerModel infoManager = null;
				//	if (Common.Data.Manager.ContainsKey(result.UserID))
				//		Common.Data.Manager.TryGetValue(result.UserID, out infoManager);
				//	if (infoManager != null)
				//	{
				//		string t1 = ProtocolHelper.ReadPackageBody(body);
				//		logger.Info("MinerLoginCommServer 存在Manager缓存" + Environment.NewLine + t1);

				//		//管理端之前已登录时执行
				//		byte[] sendBytes = ProtocolHelper.WritePackage(ProtoType.MinerManagerDownToNodeServer, TomNet.Utility.Data.JsonHelper.ToJson(infoManager.NodeServerConnId));
				//		this.server.Send((IntPtr)infoManager.CommServerConnId, sendBytes, sendBytes.Length);

				//		//清除管理端信息
				//		Common.Data.Manager.TryRemove(result.UserID, out Models.ManagerModel model);
				//	}

				//	//管理端之前未登录时执行
				//	Models.ManagerModel info = new Models.ManagerModel
				//	{
				//		CommServerConnId = (int)connId,
				//		NodeServerConnId = result.connID,
				//		UserId = result.UserID,
				//		UserName = result.Name,
				//	};

				//	string t = ProtocolHelper.ReadPackageBody(body);
				//	logger.Info("MinerLoginCommServer 存储Manager缓存" + Environment.NewLine + t);

				//	Common.Data.Manager.AddOrUpdate(info.UserId, info, (FKey, FValue) => { return info; });
				//}

				#endregion

				#endregion

				#region 手机端登录

				if (result.ClientType == ClientType.AppClient)
				{
					List<Models.ManagerModel> infosApp = null;
					if (Common.Data.App.ContainsKey(result.UserID))
						Common.Data.App.TryGetValue(result.UserID, out infosApp);

					//检测是否存在相同数据，清除相同数据
					if (infosApp.Count > 0)
					{
						int index = infosApp.FindIndex(p => p.CommServerConnId == (int)connId && p.NodeServerConnId == result.connID);
						if (index >= 0)
							infosApp.RemoveAt(index);
					}

					//缓存管理端信息
					Models.ManagerModel info = new Models.ManagerModel
					{
						CommServerConnId = (int)connId,
						NodeServerConnId = result.connID,
						UserId = result.UserID,
						UserName = result.Name,
					};

					if (infosApp != null)
						infosApp.Add(info);
					else
						infosApp = new List<Models.ManagerModel> { info };

					Common.Data.App.AddOrUpdate(info.UserId, infosApp, (FKey, FValue) => { return infosApp; });
				}

				#region 互踢操作【暂未启用】

				//if (result.ClientType == ClientType.AppClient)
				//{
				//	Models.ManagerModel infoApp = null;
				//	if (Common.Data.App.ContainsKey(result.UserID))
				//		Common.Data.App.TryGetValue(result.UserID, out infoApp);
				//	if (infoApp != null)
				//	{
				//		string t1 = ProtocolHelper.ReadPackageBody(body);
				//		logger.Info("MinerLoginCommServer 存在App缓存" + Environment.NewLine + t1);

				//		//手机端之前已登录时执行
				//		byte[] sendBytes = ProtocolHelper.WritePackage(ProtoType.MinerManagerDownToNodeServer, TomNet.Utility.Data.JsonHelper.ToJson(infoApp.NodeServerConnId));
				//		this.server.Send((IntPtr)infoApp.CommServerConnId, sendBytes, sendBytes.Length);

				//		//清除手机端信息
				//		Common.Data.Manager.TryRemove(result.UserID, out Models.ManagerModel model);
				//	}

				//	//手机端之前未登录时执行
				//	Models.ManagerModel info = new Models.ManagerModel
				//	{
				//		CommServerConnId = (int)connId,
				//		NodeServerConnId = result.connID,
				//		UserId = result.UserID,
				//		UserName = result.Name,
				//	};

				//	string t = ProtocolHelper.ReadPackageBody(body);
				//	logger.Info("MinerLoginCommServer 存储App缓存" + Environment.NewLine + t);

				//	Common.Data.App.AddOrUpdate(info.UserId, info, (FKey, FValue) => { return info; });
				//}

				#endregion

				#endregion

				#region 矿机登录

				if (result.ClientType == ClientType.MinerClient)
				{
					Models.MinerModel infoMiner = new Models.MinerModel
					{
						CommServerConnId = (int)connId,
						NodeServerConnId = result.connID,
						UserId = result.UserID,
						UserName = result.Name,
						MinerId = result.MinerID,
						MinerName = result.MinerName,
					};
					Common.Data.Miner.AddOrUpdate(infoMiner.MinerId, infoMiner, (FKey, FValue) => { return infoMiner; });

					#region 管理端，下发矿机登录信息给NodeServer

					List<Models.ManagerModel> infosManager = null;
					if (Common.Data.Manager.ContainsKey(result.UserID))
						Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

					if (infosManager != null && infosManager.Count > 0)
					{
						foreach (Models.ManagerModel info in infosManager)
						{
							McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
							{
								connID = info.NodeServerConnId,
								MinerID = result.MinerID,
								MinerName = result.MinerName
							};

							byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.CommServerMinerLoginToNodeServer, sendInfo);
							this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
						}
					}

					#endregion

					#region 手机端，下发矿机登录信息给NodeServer

					List<Models.ManagerModel> infosApp = null;
					if (Common.Data.App.ContainsKey(result.UserID))
						Common.Data.App.TryGetValue(result.UserID, out infosApp);

					if (infosApp != null && infosApp.Count > 0)
					{
						foreach (Models.ManagerModel info in infosApp)
						{
							McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
							{
								connID = info.NodeServerConnId,
								MinerID = result.MinerID,
								MinerName = result.MinerName
							};

							byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.CommServerMinerLoginToNodeServer, sendInfo);
							this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
						}
					}

					#endregion
				}

				#endregion
			}
			catch (Exception e)
			{
				logger.Error("MinerLoginCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerCloseCommServer(IntPtr connId, byte[] body) 管理端/矿机掉线通知CommServer

		private void ProcessMinerCloseCommServer(IntPtr connId, byte[] body)
		{
			//MinerCloseCommServer = 49 管理端/矿机掉线通知CommServer
			//CommServerMinerLogoutToNodeServer = 57 CommServer下发矿机离线信息给NodeServer
			//MinerCloseToNodeServerr = 75 掉线通知NodeServer

			try
			{
				McsUtility.Models.LoginResultForCommServerModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.LoginResultForCommServerModel>(body);

				string json = ProtocolHelper.ReadPackageBody(body);
				//logger.Info("MinerCloseCommServer 接收 ConnId:" + connId.ToInt32() + Environment.NewLine + json);

				#region 管理端掉线

				if (result.ClientType == ClientType.AdminClient)
				{
					List<Models.ManagerModel> infosManager = null;
					if (Common.Data.Manager.ContainsKey(result.UserID))
						Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

					if (infosManager != null && infosManager.Count > 0)
					{
						int index = infosManager.FindIndex(p => p.NodeServerConnId == result.connID);
						if (index >= 0)
							infosManager.RemoveAt(index);
					}

					if (infosManager == null)
						infosManager = new List<Models.ManagerModel>();

					Common.Data.Manager.AddOrUpdate(result.UserID, infosManager, (FKey, FValue) => { return infosManager; });
				}

				#endregion

				#region 手机端掉线

				if (result.ClientType == ClientType.AppClient)
				{
					List<Models.ManagerModel> infosAPP = null;
					if (Common.Data.App.ContainsKey(result.UserID))
						Common.Data.App.TryGetValue(result.UserID, out infosAPP);

					if (infosAPP != null && infosAPP.Count > 0)
					{
						int index = infosAPP.FindIndex(p => p.NodeServerConnId == result.connID);
						if (index >= 0)
							infosAPP.RemoveAt(index);
					}

					if (infosAPP == null)
						infosAPP = new List<Models.ManagerModel>();

					Common.Data.App.AddOrUpdate(result.UserID, infosAPP, (FKey, FValue) => { return infosAPP; });
				}

				#endregion

				#region 矿机掉线

				if (result.ClientType == ClientType.MinerClient)
				{
					//是否存在当前矿机Id的NodeServer连接Id相符信息，连接Id相同时通知75，否则忽略
					bool hasMiner = false;
					foreach (int key in Common.Data.Miner.Keys)
					{
						if (Common.Data.Miner[key].MinerId == result.MinerID
							&& Common.Data.Miner[key].NodeServerConnId == result.connID
							&& Common.Data.Miner[key].CommServerConnId == (int)connId
							)
						{
							hasMiner = true;
							break;
						}
					}

					if (hasMiner)
					{
						Common.Data.Miner.TryRemove(result.MinerID, out Models.MinerModel infoMiner);

						//通知NodeServer
						byte[] sendNodeServerBytes = ProtocolHelper.WritePackage(ProtoType.MinerCloseToNodeServerr, body);
						this.server.Send(connId, sendNodeServerBytes, sendNodeServerBytes.Length);

						#region 管理端，下发矿机离线信息给NodeServer

						List<Models.ManagerModel> infosManager = null;
						if (Common.Data.Manager.ContainsKey(result.UserID))
							Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

						if (infosManager != null && infosManager.Count > 0 && infoMiner != null)
						{
							foreach (Models.ManagerModel info in infosManager)
							{
								McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
								{
									connID = info.NodeServerConnId,
									MinerID = result.MinerID,
									MinerName = result.MinerName
								};

								byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.CommServerMinerLogoutToNodeServer, sendInfo);
								this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
							}
						}

						#endregion

						#region 手机端，下发矿机离线信息给NodeServer

						List<Models.ManagerModel> infosApp = null;
						if (Common.Data.App.ContainsKey(result.UserID))
							Common.Data.App.TryGetValue(result.UserID, out infosApp);

						if (infosApp != null && infosApp.Count > 0 && infoMiner != null)
						{
							foreach (Models.ManagerModel info in infosApp)
							{
								McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
								{
									connID = info.NodeServerConnId,
									MinerID = result.MinerID,
									MinerName = result.MinerName
								};

								byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.CommServerMinerLogoutToNodeServer, sendInfo);
								this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
							}
						}

						#endregion
					}
				}

				#endregion
			}
			catch (Exception e)
			{
				logger.Error("MinerCloseCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerClientStartMiningToCommServer(IntPtr connId, byte[] body) 矿机挖矿状态发送给CommServer

		private void ProcessMinerClientStartMiningToCommServer(IntPtr connId, byte[] body)
		{
			//MinerClientStartMiningToCommServer = 59 矿机挖矿状态发送给CommServer
			//MinerClientStartMiningToNodeServer = 60 矿机挖矿状态下发给NodeServer

			try
			{
				McsUtility.Models.MinersModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.MinersModel>(body);

				string json = ProtocolHelper.ReadPackageBody(body);
				//logger.Info("MinerClientStartMiningToCommServer 接收 ConnId:" + connId.ToInt32() + Environment.NewLine + json);

				//更新矿机缓存数据
				Models.MinerModel infoMiner = null;
				if (Common.Data.Miner.ContainsKey(result.ID))
				{
					Common.Data.Miner.TryGetValue(result.ID, out infoMiner);
					if (infoMiner != null)
						infoMiner.Model = result;
				}

				#region 管理端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosManager = null;
				if (Common.Data.Manager.ContainsKey(result.UserID))
					Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

				if (infosManager != null && infosManager.Count > 0)
				{
					foreach (Models.ManagerModel info in infosManager)
					{
						McsUtility.Models.MinerClientStatusModel sendInfo = new McsUtility.Models.MinerClientStatusModel
						{
							ConnId = info.NodeServerConnId,
							MinerId = result.ID,
							MinerName = result.MinerName,
							Model = result
						};

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("MinerClientStartMiningToCommServer 管理端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(sendInfo);
						//logger.Info("MinerClientStartMiningToCommServer 管理端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerClientStatusModel>(ProtoType.MinerClientStartMiningToNodeServer, sendInfo);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion

				#region 手机端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosApp = null;
				if (Common.Data.App.ContainsKey(result.UserID))
					Common.Data.App.TryGetValue(result.UserID, out infosApp);

				if (infosApp != null && infosApp.Count > 0)
				{
					foreach (Models.ManagerModel info in infosApp)
					{
						McsUtility.Models.MinerClientStatusModel sendInfo = new McsUtility.Models.MinerClientStatusModel
						{
							ConnId = info.NodeServerConnId,
							MinerId = result.ID,
							MinerName = result.MinerName,
							Model = result
						};

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("MinerClientStartMiningToCommServer 手机端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(sendInfo);
						//logger.Info("MinerClientStartMiningToCommServer 手机端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerClientStatusModel>(ProtoType.MinerClientStartMiningToNodeServer, sendInfo);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion
			}
			catch (Exception e)
			{
				logger.Error("MinerClientStartMiningToCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerManagerStartMiningCommServer(IntPtr connId, byte[] body) 开始挖矿转到CommServer

		private void ProcessMinerManagerStartMiningCommServer(IntPtr connId, byte[] body)
		{
			//MinerManagerStartMiningCommServer = 35 开始挖矿转到CommServer
			//CommServerMinerManagerStartMining = 39 CommServer下发开始挖矿指令

			try
			{
				int[] ids = McsUtility.ProtocolHelper.ReadPackageBody<int[]>(body);

				//矿机信息处理
				Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
				foreach (int id in ids)
				{
					if (id > 0)
					{
						Models.MinerModel info = null;
						if (Common.Data.Miner.ContainsKey(id))
							Common.Data.Miner.TryGetValue(id, out info);

						if (info != null)
						{
							if (dics.ContainsKey(info.CommServerConnId))
								dics[info.CommServerConnId].Add(info.MinerId);
							else
								dics.Add(info.CommServerConnId, new List<int> { info.MinerId });
						}
					}
				}

				//发送指令
				foreach (int key in dics.Keys)
				{
					byte[] sendBytes = ProtocolHelper.WritePackage<int[]>(ProtoType.CommServerMinerManagerStartMining, dics[key].ToArray());
					this.server.Send((IntPtr)key, sendBytes, sendBytes.Length);
				}
			}
			catch (Exception e)
			{
				logger.Error("MinerManagerStartMiningCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerManagerEndMiningCommServer(IntPtr connId, byte[] body) 停止挖矿转到CommServer

		private void ProcessMinerManagerEndMiningCommServer(IntPtr connId, byte[] body)
		{
			//MinerManagerEndMiningCommServer = 36 停止挖矿转到CommServer
			//CommServerMinerManagerEndMining = 40 CommServer下发停止挖矿指令

			try
			{
				int[] ids = McsUtility.ProtocolHelper.ReadPackageBody<int[]>(body);

				//矿机信息处理
				Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
				foreach (int id in ids)
				{
					if (id > 0)
					{
						Models.MinerModel info = null;
						if (Common.Data.Miner.ContainsKey(id))
							Common.Data.Miner.TryGetValue(id, out info);

						if (info != null)
						{
							if (dics.ContainsKey(info.CommServerConnId))
								dics[info.CommServerConnId].Add(info.MinerId);
							else
								dics.Add(info.CommServerConnId, new List<int> { info.MinerId });
						}
					}
				}

				//发送指令
				foreach (int key in dics.Keys)
				{
					byte[] sendBytes = ProtocolHelper.WritePackage<int[]>(ProtoType.CommServerMinerManagerEndMining, dics[key].ToArray());
					this.server.Send((IntPtr)key, sendBytes, sendBytes.Length);
				}
			}
			catch (Exception e)
			{
				logger.Error("MinerManagerEndMiningCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerManagerResetCommServer(IntPtr connId, byte[] body) 重启机器转到CommServer

		private void ProcessMinerManagerResetCommServer(IntPtr connId, byte[] body)
		{
			//MinerManagerResetCommServer = 37 重启机器转到CommServer
			//CommServerMinerManagerReset = 41 CommServer下发重启机器指令

			try
			{
				int[] ids = McsUtility.ProtocolHelper.ReadPackageBody<int[]>(body);

				//矿机信息处理
				Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
				foreach (int id in ids)
				{
					if (id > 0)
					{
						Models.MinerModel info = null;
						if (Common.Data.Miner.ContainsKey(id))
							Common.Data.Miner.TryGetValue(id, out info);

						if (info != null)
						{
							if (dics.ContainsKey(info.CommServerConnId))
								dics[info.CommServerConnId].Add(info.MinerId);
							else
								dics.Add(info.CommServerConnId, new List<int> { info.MinerId });
						}
					}
				}

				//发送指令
				foreach (int key in dics.Keys)
				{
					byte[] sendBytes = ProtocolHelper.WritePackage<int[]>(ProtoType.CommServerMinerManagerReset, dics[key].ToArray());
					this.server.Send((IntPtr)key, sendBytes, sendBytes.Length);
				}
			}
			catch (Exception e)
			{
				logger.Error("MinerManagerResetCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerManagerModifyBatchCommServer(IntPtr connId, byte[] body) 批量修改矿池转到CommServer【暂未启用】

		private void ProcessMinerManagerModifyBatchCommServer(IntPtr connId, byte[] body)
		{
			//MinerManagerModifyBatchCommServer = 63 批量修改矿池转到CommServer
			//CommServerMinerManagerModifyBatch = 64 CommServer下发批量修改矿池指令

			try
			{
				//矿机信息处理
				Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();

				McsUtility.Models.MinersMiningRequestModel result = McsUtility.ProtocolHelper.ReadPackageBody<McsUtility.Models.MinersMiningRequestModel>(body);
				foreach (int id in result.MinersIDs)
				{
					if (id > 0)
					{
						Models.MinerModel info = null;
						if (Common.Data.Miner.ContainsKey(id))
							Common.Data.Miner.TryGetValue(id, out info);

						if (info != null)
						{
							McsUtility.Models.MinersMiningRequestModel sendInfo = new McsUtility.Models.MinersMiningRequestModel
							{
								MinersIDs = new List<int> { id },
								AutoName = false,
								AutoRun = result.AutoRun,
								AutoUpdate = result.AutoUpdate,
								MinerName = result.MinerName,
								FirstCoin = result.FirstCoin,
								FirstPool = result.FirstPool,
								FirstWallet = result.FirstWallet,
								Second = result.Second,
								SecondCoin = result.SecondCoin,
								SecondPool = result.SecondPool,
								SecondWallet = result.SecondWallet,
								MinFan = result.MinFan,
								MaxFan = result.MaxFan,
								Dcri = result.Dcri,
								AdvParams = result.AdvParams
							};
							byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinersMiningRequestModel>(ProtoType.CommServerMinerManagerModifyBatch, sendInfo);
							this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
						}
					}
				}
			}
			catch (Exception e)
			{
				logger.Error("MinerManagerModifyBatchCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerManagerModifyOrePoolUrlCommServer(IntPtr connId, byte[] body) 修改矿池转到CommServer（高级配置）

		private void ProcessMinerManagerModifyOrePoolUrlCommServer(IntPtr connId, byte[] body)
		{
			//MinerManagerModifyOrePoolUrlCommServer = 34 修改矿池转到CommServer（高级配置）
			//CommServerMinerManagerModifyOrePoolUrl = 38 CommServer下发修改矿池指令（高级配置）

			try
			{
				//矿机信息处理
				Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();

				McsUtility.Models.MinersMiningRequestModel result = McsUtility.ProtocolHelper.ReadPackageBody<McsUtility.Models.MinersMiningRequestModel>(body);
				foreach (int id in result.MinersIDs)
				{
					if (id > 0)
					{
						Models.MinerModel info = null;
						if (Common.Data.Miner.ContainsKey(id))
							Common.Data.Miner.TryGetValue(id, out info);

						if (info != null)
						{
							if (dics.ContainsKey(info.CommServerConnId))
								dics[info.CommServerConnId].Add(info.MinerId);
							else
								dics.Add(info.CommServerConnId, new List<int> { info.MinerId });
						}
					}
				}

				//发送指令
				McsUtility.Models.MinersMiningRequestModel sendInfo = McsUtility.ProtocolHelper.ReadPackageBody<McsUtility.Models.MinersMiningRequestModel>(body);
				foreach (int key in dics.Keys)
				{
					sendInfo.MinersIDs = dics[key];
					byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinersMiningRequestModel>(ProtoType.CommServerMinerManagerModifyOrePoolUrl, sendInfo);
					this.server.Send((IntPtr)key, sendBytes, sendBytes.Length);
				}
			}
			catch (Exception e)
			{
				logger.Error("MinerManagerModifyOrePoolUrlCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessMinerClientEndMiningToCommServer(IntPtr connId, byte[] body) 矿机停止挖矿状态发送给CommServer

		private void ProcessMinerClientEndMiningToCommServer(IntPtr connId, byte[] body)
		{
			//MinerClientEndMiningToCommServer = 68 矿机停止挖矿状态发送给CommServer
			//MinerClientEndMiningToNodeServer = 69 矿机停止挖矿状态下发给NodeServer

			try
			{
				McsUtility.Models.MinerCloseModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.MinerCloseModel>(body);
				string json = ProtocolHelper.ReadPackageBody(body);
				//logger.Info("MinerClientEndMiningToCommServer 接收 ConnId:" + connId.ToInt32() + Environment.NewLine + json);

				#region 管理端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosManager = null;
				if (Common.Data.Manager.ContainsKey(result.UserID))
					Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

				if (infosManager != null && infosManager.Count > 0)
				{
					foreach (Models.ManagerModel info in infosManager)
					{
						McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
						{
							connID = info.NodeServerConnId,
							MinerID = result.MinerID,
							MinerName = result.MinerName
						};

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("MinerClientEndMiningToCommServer 管理端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(sendInfo);
						//logger.Info("MinerClientEndMiningToCommServer 管理端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.MinerClientEndMiningToNodeServer, sendInfo);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion

				#region 手机端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosApp = null;
				if (Common.Data.App.ContainsKey(result.UserID))
					Common.Data.App.TryGetValue(result.UserID, out infosApp);

				if (infosApp != null && infosApp.Count > 0)
				{
					foreach (Models.ManagerModel info in infosApp)
					{
						McsUtility.Models.MinerCloseModel sendInfo = new McsUtility.Models.MinerCloseModel
						{
							connID = info.NodeServerConnId,
							MinerID = result.MinerID,
							MinerName = result.MinerName
						};

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("MinerClientEndMiningToCommServer 手机端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(sendInfo);
						//logger.Info("MinerClientEndMiningToCommServer 手机端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.MinerCloseModel>(ProtoType.MinerClientEndMiningToNodeServer, sendInfo);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion
			}
			catch (Exception e)
			{
				logger.Error("MinerClientEndMiningToCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessGetNodeServerCommServer(IntPtr connId, byte[] body) 向CommServer询问可连接NodeServer

		private void ProcessGetNodeServerCommServer(IntPtr connId, byte[] body)
		{
			//GetNodeServerCommServer = 50 向CommServer询问可连接NodeServer
			//CommServerGetNodeServer = 51 CommServer响应询问可连接NodeServer

			try
			{
				int result = ProtocolHelper.ReadPackageBody<int>(body);

				List<Models.NodeServerModel> infos = new List<Models.NodeServerModel>();
				foreach (int key in Common.Data.NodeServer.Keys)
				{
					//询问的NodeServer排除掉
					if (Common.Data.NodeServer[key].CommServerConnId != (int)connId)
						infos.Add(Common.Data.NodeServer[key]);
				}

				McsUtility.Models.GetNodeServerResponseModel sendInfo = new McsUtility.Models.GetNodeServerResponseModel();
				if (infos.Count > 0)
				{
					sendInfo = new McsUtility.Models.GetNodeServerResponseModel
					{
						ConnID = result,//管理端连接NodeServerId
						IP = infos[0].IP,
						Port = infos[0].Port
					};
				}

				byte[] sendBytes = ProtocolHelper.WritePackage<McsUtility.Models.GetNodeServerResponseModel>(ProtoType.CommServerGetNodeServer, sendInfo);
				this.server.Send(connId, sendBytes, sendBytes.Length);
			}
			catch (Exception e)
			{
				logger.Error("GetNodeServerCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#region void ProcessNodeServerWarnToCommServer(IntPtr connId, byte[] body) 报警转交CommServer

		private void ProcessNodeServerWarnToCommServer(IntPtr connId, byte[] body)
		{
			//NodeServerWarnToCommServer = 87 报警转交CommServer
			//CommServerWarnToNodeServer = 88 CommServer下发报警信息

			try
			{
				McsUtility.Models.WarnModel result = ProtocolHelper.ReadPackageBody<McsUtility.Models.WarnModel>(body);
				string json = ProtocolHelper.ReadPackageBody(body);
				//logger.Info("NodeServerWarnToCommServer 接收 ConnId:" + connId.ToInt32() + Environment.NewLine + json);

				#region 管理端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosManager = null;
				if (Common.Data.Manager.ContainsKey(result.UserID))
					Common.Data.Manager.TryGetValue(result.UserID, out infosManager);

				if (infosManager != null && infosManager.Count > 0)
				{
					foreach (Models.ManagerModel info in infosManager)
					{
						result.ConnID = info.NodeServerConnId;

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("NodeServerWarnToCommServer 管理端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(result);
						//logger.Info("NodeServerWarnToCommServer 管理端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage(ProtoType.CommServerWarnToNodeServer, result);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion

				#region 手机端，下发矿机信息给NodeServer

				List<Models.ManagerModel> infosApp = null;
				if (Common.Data.App.ContainsKey(result.UserID))
					Common.Data.App.TryGetValue(result.UserID, out infosApp);

				if (infosApp != null && infosApp.Count > 0)
				{
					foreach (Models.ManagerModel info in infosApp)
					{
						result.ConnID = info.NodeServerConnId;

						//string t = TomNet.Utility.Data.JsonHelper.ToJson(info);
						//logger.Info("NodeServerWarnToCommServer 手机端信息:" + Environment.NewLine + t);

						//t = TomNet.Utility.Data.JsonHelper.ToJson(result);
						//logger.Info("NodeServerWarnToCommServer 手机端发送 ID:" + info.CommServerConnId + Environment.NewLine + t);

						byte[] sendBytes = ProtocolHelper.WritePackage(ProtoType.CommServerWarnToNodeServer, result);
						this.server.Send((IntPtr)info.CommServerConnId, sendBytes, sendBytes.Length);
					}
				}

				#endregion
			}
			catch (Exception e)
			{
				logger.Error("NodeServerWarnToCommServer" + Environment.NewLine + e);
			}
		}

		#endregion

		#endregion

		#region protected virtual HandleResult OnServerClose(IntPtr connId, SocketOperation enOperation, int errorCode)

		protected virtual HandleResult OnServerClose(IntPtr connId, SocketOperation enOperation, int errorCode)
		{
			if (errorCode == 0)
			{
				AddMsg(string.Format(" > [{0},OnClose]", connId));
			}
			else
			{
				AddMsg(string.Format(" > [{0},OnError] -> OP:{1},CODE:{2}", connId, enOperation, errorCode));
			}
			int ConnID = connId.ToInt32();
			if (server.RemoveExtra(connId) == false)
			{
				AddMsg(string.Format(" > [{0},OnClose] -> SetConnectionExtra({0}, null) fail", connId));
			}

			try
			{
				//删除NodeServer信息
				Common.Data.NodeServer.TryRemove(ConnID, out Models.NodeServerModel model);

				string content = "【超级矿工】其中一台NodeServer离线。";
				if (model != null)
					content = "【超级矿工】IP为 " + model.IP + " 的NodeServer离线。";

				//发送短信
				string phones = System.Configuration.ConfigurationManager.AppSettings["Phone"];
				McsUtility.SendMessage.SendMessge(phones, content);
			}
			catch (Exception e)
			{
				logger.Error("OnServerClose" + Environment.NewLine + e);
			}

			return HandleResult.Ok;
		}

		#endregion

		#region protected virtual HandleResult OnServerShutdown()

		protected virtual HandleResult OnServerShutdown()
		{
			AddMsg(" > [OnServerShutdown]");
			return HandleResult.Ok;
		}

		#endregion

		#endregion

		#region 守护线程

		private void StartObserver()
		{
			StopObserver();
			ObserverTokenSource = new CancellationTokenSource();
			ObserverTask = Task.Factory.StartNew(ObserverWork, ObserverTokenSource.Token);
		}

		private void StopObserver()
		{
			if (ObserverTokenSource != null)
			{
				if (ObserverTask.Status == TaskStatus.Running)
				{
					ObserverTokenSource.Cancel();
				}
			}
		}

		private void ObserverWork()
		{
			//判断是否取消任务
			while (!ObserverTokenSource.IsCancellationRequested)
			{
				try
				{

				}
				catch
				{

				}
				Thread.Sleep(10 * 1000);
			}
		}

		#endregion
	}
}
